FPGA极易入门教程 您所在的位置:网站首页 二进制 1234 FPGA极易入门教程

FPGA极易入门教程

2024-01-23 14:57| 来源: 网络整理| 查看: 265

写在前面

       接上篇:FPGA极易入门教程(1)----数码管静态显示篇,建议一起阅读,有助于理解。

1、动态显示原理

       在静态显示章节我们实现了6个数码管的显示,在所有数码管的位选信号都选通的情况下,6个数码管显示都是一致的。这就有点难搞了,我在实际开发中怎么可能用6个数码管来显示同一个数字,我用一个不就得了?所以说数码管的静态显示这种方法不太实用,仅仅能帮助我们如何学习使用FPGA来控制数码管。看来得想点办法让6个数码管显示不同的数字。

       大家应该都清楚电影的基本显示原理:视觉暂留。科学实验证明,人眼在某个视像消失后,仍可使该物像在视网膜上滞留0.1-0.4秒左右。电影胶片以每秒24格画面匀速转动,一系列静态画面就会因视觉暂留作用而造成一种连续的视觉印象,产生逼真的动感。

        我们的开发板一般都是独立控制6个数码管的电源(位选),然后可以控制所有的数码管的显示内容(段选)。假设现在需要显示一个数字:123456。那么可以先让数码管显示6,由于所有数码管的段选均连在一起,假设所有数码管供电(位选控制),那么数码管应该会显示:666666,这显然不符合我们的需求。可以只保留最右边的数码管亮,其他全部熄灭,那么6个数码管应该显示:、、、、、6(、表示数码管熄灭),让这个状态先保持一定的时间,比如说1ms。接着,让数码管显示5,同时除了右数第二个数码管供电外,其他数码管一概熄灭,那么6个数码管应该显示:、、、、5、(、表示数码管熄灭),这个状态仍然保持1ms。然后重复这个过程,来使得剩下的4个数码管分别显示4~1。

       显然,由于人眼的“视觉暂留”效应,第一次看到的数字“6”会保持一定的时间,第二次看到的数字“5”也会保持一定的时间,```````最后一次看到的数字“1”也会保持一定的时间,这样看起来就好像数字“123456”同时出现在了眼前。

        上图是显示数字“1234”的示意GIF,因为切换速度不够快,所以还是可以看到明显的切换过程,但是实际上已经有一点“暂留“的感觉了。可以预见,一旦这个切换速度够快,那么人眼应该是无法察觉切换过程的,也就是说到时只能看到数字”1234“了,就好像数字”1234“同时在显示一样。依靠这个原理我们就可以实现数码管的动态显示了。

2、动态显示驱动

       了解了原理后,开始编写数码管动态显示的驱动代码。

2.1、端口

        驱动模块输入输出端口如下图:

        各信号含义如下:

                输入端:

                        sys_clk:系统时钟,我的开发板是50M,周期20ns

                        sys_rst_n:低电平有效的异步复位信号

                        num[19:0]:显示的数字,10进制,可显示范围:0-999999,需要20bit的位宽才能表示

                输出端

                        dis_seg[6:0]:数码管7位段选,从高到底分别控制二极管a~g,低电平有效

                        dis_sel[5:0]:数码管位宽,共6个数码管所以共需要6bit,低电平有效

                        dp:小数点二极管控制信号,低电平有效

2.2、Verilog代码 2.2.1、驱动模块代码

       不妨先构思一下具体的思路:

计时(计数)模块

        让数码管实现动态显示的效果的关键,是要以一个较快的时间来切换供电的数码管,这个时间一般可以设置为1ms或者其他。那么我们必然需要一个计时模块来计数,这个模块重复计时到1ms。

移位模块

        让数码管实现动态显示的效果的另一个关键,是要每隔1ms让一个数码管来显示数字的对应位,此时其他位的数码管必须均灭。比如第1ms显示个位的数码管,下一ms显示十位的数码管······直到显示十万位的数码管后再循环。而数码管的亮灭是使用位选信号控制的,也就是说我们需要控制位选信号的移动,每隔1ms,让位选信号左移1位即可。

BCD转换模块

        在本设计里,输入的是十进制数,因为这样使用起来是比较直观和方便的。但是使用者方便了,FPGA就不一定“方便”了。在数字世界中,数字的表示都是采用二进制数来表示。那么如果想要完整的表示十进制中的10个单个数字0~9,则最少需要用4位二进制表示(‘d9 == 'b1001)。但是直接用4位二级制表示,也会有显示问题。比如我现在输入数字:15(’d15 == 'hf),那么我肯定希望数码管显示的是两个数字1--5,而不是一个字母F吗,毕竟喜欢在日常生活中用十六进制的人应该不多。所以我们需要将显示数字从二进制转换为BCD进制码。BCD码可以简单地理解为,将所以位一一拆开,每位均用4位2进制码表示。例如十进制的123,BCD码则为:0001_0010_0011。

        二进制码转BCD码的方法有很多(以后再说),本文采用一种比较直观(但是资源消耗很多)的方法:通过求商、求余的方法来求得6位数的每一位。

        求得6位数的每一位(共6位)后,还需要从高位到低位依次进行判断:

                该位是否非0。若所有位均不为0,则应是完整的6位数,此时将各个位上的BCD码统一寄存到一个变量。

                若最高位为0,则说明这是一个5位数。在这里显示5位数的时候,我们希望最高位会被熄灭,而不是显示0,比如012345就看起来很别扭。所以最高位的BCD码(也就是0)就不用被寄存,替代寄存一个其他约定好的数('ha~'hf均可,这里用'ha)来表示熄灭,后面将BCD码解析成数码管的编码时,就接卸成熄灭所有段选。或者直接用熄灭的数码管编码代替,省略一道解码过程。

                若最高2位均为0,则说明这个是一个4位数。最高位、此高位的BCD码均不用寄存,用熄灭的BCD码代替。

                其他位情况相似。

判断模块

        移位模块让位选有效信号定时定时移动了起来。在判断模块,需要根据当前有效的位选信号,来判断对应数码管应该显示什么数值。例如,在当前时刻,仅右数第一个数码管点亮,那么此时数码管应该显示的值即为高位数的BCD码······

译码模块

        根据输入的BCD码来让数码管点亮对应的段选信号,可以看作是BCD码到段选码的译码过程。

        根据以上,可以编写出完整的代码如下:

//6位8段式数码管动态显示驱动 //端口定义 module dis_dyn_dri ( input sys_clk , //时钟信号 input sys_rst_n , //复位信号(低有效) input [19:0] num, //数码管显示的十进制数,10进制,范围0-999999 output reg [5:0] dis_sel, //数码管位选 output reg [6:0] dis_seg, //数码管段选 output dis_dp //数码管小数点 ); reg [31:0] cnt_1ms; //1ms计数器 reg [23:0] split_num; //拆分的数字 reg [3:0] dis_num; //显示的数字 wire [3:0] num_r1; //右数第1位,即个位 wire [3:0] num_r2; //右数第2位,即十位 wire [3:0] num_r3; //右数第3位,即百位 wire [3:0] num_r4; //右数第4位,即千位 wire [3:0] num_r5; //右数第5位,即万位 wire [3:0] num_r6; //右数第6位,即十万位 //NO.1------------------------------------------------------------------------------------------------------------ //1ms计时模块,1ms/20ns=50_000,从0开始,只要计数到49999 always @ (posedge sys_clk or negedge sys_rst_n) begin if (!sys_rst_n) cnt_1ms


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有